/*
 * Copyright 2008 Max Kugland
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.splink.library.distributor {

	/**
	 * Default <code>IDistributor</code> implementation
	 * 
	 * @author Max Kugland
	 */
	public class Distributor implements IDistributor {
		private var _listeners : Array = new Array();
		private var _fresh : Array = new Array();

		public function Distributor() {
		}

		/**
		 * If more then one listener is registered, the event distribution order is defined 
		 * by the registration order. The first registered listener gets the event first. 
		 * 
		 * @inheritDoc
		 */
		public function register(type : Dtype, fnc : Function, params : Object = null, priority : uint = uint.MIN_VALUE) : IDistributor {
			if(getListener(type, fnc, _listeners) == null) {
				
				var i : int = 0;
				for each (var listener : Listener in _listeners) {
					if(listener.priority < priority) {
						break;
					}
					i++;
				}
				var ltr : Listener = new Listener(type, fnc, params, priority);
				_listeners.splice(i, 0, ltr);
				_fresh.push(ltr);
			}
			
			return this;
		}

		/**
		 * @inheritDoc
		 */
		public function unregister(type : Dtype, fnc : Function) : IDistributor {
			var listener : Listener = getListener(type, fnc, _listeners);
			if(listener) {
				var idx : int = _listeners.indexOf(listener);
				if(idx != -1) {
					_listeners.splice(idx, 1);
				}
			}
			return this;
		}
		
		/**
		 * @inheritDoc
		 */
		public function distribute(event : IDevent) : void {
			_fresh = new Array();
			
			// copy the array in case something is unregistered during the distribute phase and messes up the _listeners array
			var type : Dtype = event.type;
			var copy : Array = _listeners.concat();
			for each (var listener : Listener in copy) {
				
				// check whether listeners have been removed during the distribution phase and ignore the removed listeners
				if(_listeners.indexOf(listener) == -1) {
					continue;
				}
				// if new listeners hav been added during the distribution phase, these listeners are added to the currently running dispatch
				if(_fresh.length > 0) {
					for each (var f : Listener in _fresh) {
						if(_listeners.indexOf(f) == -1) {
							copy.push(f);
						}
					}
				}
				
				if(listener.type == type) {
					if(listener.params) {
						listener.fnc(event, listener.params);
					} else {
						listener.fnc(event);
					}
				}
			}
		}
		
		private static function getListener(type : Dtype, fnc : Function, listeners : Array) : Listener {
			for each (var listener : Listener in listeners) {
				if(listener.type == type) {
					if(listener.fnc == fnc) {
						return listener;
					}
				}
			}
			return null;
		}

		/**
		 * Unregisters all previously registred <code>Listener</code> objects. In case you use
		 * the standard implementation <code>Distributor</code> it is still ready for usage after
		 * you called the finalize method, because it only the listeners are cleared. 
		 * 
		 * @inheritDoc
		 */
		public function finalize() : void {
			_listeners = new Array();
		}
	}
}

import org.splink.library.distributor.Dtype;

internal class Listener {
	/**
	 * The type of the listener @see IDevent
	 */
	public var type : Dtype;
	/**
	 * The function which gets invoked if the <code>IDevent</code> is fired
	 * @see IDevent
	 */
	public var fnc : Function;
	/**
	 * An optional params object, which enables to registrant to pass local vars to the
	 * recipient
	 */
	public var params : Object;
	/**
	 * The listener's priority.
	 * @see org.splink.library.distributor.IDistributor#register
	 */
	public var priority : uint;

	public function Listener(type : Dtype, fnc : Function, params : Object = null, priority : uint = uint.MIN_VALUE) {
		this.type = type;
		this.fnc = fnc;
		this.params = params;
		this.priority = priority;
	}
}